using System;
using System.Collections.Generic;
using System.Text;
using ScrewTurn.Wiki.PluginFramework;
using ScrewTurn.Wiki;
using System.Text.RegularExpressions;
using System.Reflection;

namespace Stankovski.Wiki.FormattingPlus
{
	public class SectionTransclusion : FormatterPlusBase
	{
		private static Regex transclusion = new Regex(@"\{T(?:\:|\|)(?<page>[^}#]+)(?:\#(?<section>[^}#]+))?}", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
				
		public override string Format(string raw, ContextInformation context, FormattingPhase phase)
		{
			if (phase == FormattingPhase.Phase1)
			{
				raw = formatTransclusion(raw, context, phase);
			}			
			
			return raw;
		}

		private string formatTransclusion(string raw, ContextInformation context, FormattingPhase phase)
		{
			StringBuilder sb = new StringBuilder(raw);
			StringBuilder dummy = new StringBuilder();

			List<int> noWikiBegin = new List<int>(), noWikiEnd = new List<int>();
			int end = 0;
			ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);

			Match match = transclusion.Match(sb.ToString());
			while (match.Success)
			{
				if (!IsNoWikied(match.Index, noWikiBegin, noWikiEnd, out end))
				{
					sb.Remove(match.Index, match.Length);
					PageInfo info = Pages.Instance.FindPage(match.Groups["page"].Value);
					if (info != null && info != context.Page)
					{ // Avoid circular transclusion!
						dummy = new StringBuilder();
						dummy.Append(@"<div class=""transcludedpage"">");
						dummy.AppendLine();
						// The current PageInfo is null to disable section editing and similar features
						string subContent = Content.GetPageContent(info, true).Content;
						// Get only section
						
						if (match.Groups["section"] != null)
						{
							List<HPosition> thPos = Formatter.DetectHeaders(subContent);
							foreach (HPosition pos in thPos)
							{
								Regex header = new Regex("^[#.\\s\\d]+(?<body>" + match.Groups["section"].Value + ")\\n?", RegexOptions.Singleline);
								if (header.IsMatch(pos.Text))
								{
									int start = 0;
									int len = 0;
									string anchor = "";
									ExtractSection(subContent, pos.ID, out start, out len, out anchor);
									if (start >= 0 && len <= subContent.Length)
										subContent = subContent.Substring(start, len);
									break;
								}
							}
						}
						dummy.Append(FormattingPipeline.FormatWithPhase1And2(subContent, null));
						dummy.AppendLine();
						dummy.Append("</div>");
						sb.Insert(match.Index, dummy.ToString());
					}
				}
				ComputeNoWiki(sb.ToString(), ref noWikiBegin, ref noWikiEnd);
				match = transclusion.Match(sb.ToString(), end);
			}

			return sb.ToString();
		}

		private void ExtractSection(string content, int section, out int start, out int len, out string anchor)
		{
			List<HPosition> hPos = Formatter.DetectHeaders(content);
			start = 0;
			len = content.Length;
			anchor = "";
			int level = -1;
			bool found = false;
			for (int i = 0; i < hPos.Count; i++)
			{
				if (hPos[i].ID == section)
				{
					start = hPos[i].Index;
					len = len - start;
					level = hPos[i].Level; // Level is used to edit the current section AND all the subsections
					// Set the anchor value so that it's possible to redirect the user to the just edited section
					anchor = Formatter.BuildHAnchor(hPos[i].Text);
					found = true;
					break;
				}
			}
			if (found)
			{
				int diff = len;
				for (int i = 0; i < hPos.Count; i++)
				{
					if (hPos[i].Index > start && // Next section (Hx)
						hPos[i].Index - start < diff && // The nearest section
						hPos[i].Level <= level)
					{ // Of the same level or higher
						len = hPos[i].Index - start - 1;
						diff = hPos[i].Index - start;
					}
				}
			}
		}
	}
}
